package drds.server.sqlparser.parser;

import static drds.server.sqlparser.parser.CharType.isFirstIdentifierChar;
import static drds.server.sqlparser.parser.CharType.isIdentifierChar;
import static drds.server.sqlparser.parser.CharType.isWhitespace;
import static drds.server.sqlparser.parser.Mark.COMMA;
import static drds.server.sqlparser.parser.Mark.EOF;
import static drds.server.sqlparser.parser.Mark.LEFT_PAREN;
import static drds.server.sqlparser.parser.Mark.RIGHT_PAREN;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * DRDS创始人曹正辉-汪汪<br/>
 * 携手打造一款优秀的商业分布式数据库服务软件。
 * 
 * @see 写于2017年1月15日,今天是个特别的日子,愿每年的这天我的好兄弟都开开心心。
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class Lexer {
	static {
		System.out.println(Mark.DRDS + " sql解析词法器初始化");
	}
	protected final String sql;
	protected int index;
	protected char $char;
	protected int bufferStartIndex;
	protected int bufferSize;
	protected Mark mark;
	protected String stringValue;
	private int variableIndex = -1;

	public Lexer(String sql) {

		this.sql = sql;
		this.index = -1;
		nextChar();
	}

	protected void check(Mark mark) {
		if (this.mark != mark) {
			System.out.println("需要:" + mark + ",当前是:" + this.mark);
			throw new IllegalArgumentException(Mark.class.getSimpleName());
		}
	}

	/**
	 * [offset, offset + count)即从offset开始总共count个字符(包括offset)
	 */
	public final String subString(int offset, int count) {
		return this.sql.substring(offset, offset + count);
	}

	public int nextVariableIndex() {
		return ++this.variableIndex;
	}

	public boolean isEof() {
		return this.index >= this.sql.length();
	}

	private static final char END = 0;

	public final char charAt(int index) {
		if (index >= this.sql.length()) {
			return END;
		}

		return this.sql.charAt(index);
	}

	protected final void nextChar() {
		this.$char = charAt(++this.index);
	}

	protected void lastChar() {
		this.$char = charAt(--this.index);
	}

	protected void error() {
		this.mark = Mark.ERROR;
	}

	public final void nextMark() {
		this.bufferSize = 0;

		for (;;) {
			if (isWhitespace(this.$char)) {
				nextChar();
				continue;
			}
			if (isFirstIdentifierChar(this.$char)) {
				scanIdentifier();
				return;
			}

			switch (this.$char) {
			// 字符串
			case '\'':
				this.mark = Mark.STRING_START;
				scanString();
				return;
			case '\"':// 不允许出现特殊字符\"
				throw new UnsupportedOperationException("不支持\"");
				// 数字
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '-':
				scanNumber();
				return;
				// 分隔符
			case ',':
				nextChar();
				this.mark = COMMA;
				return;
			case '(':
				nextChar();
				this.mark = LEFT_PAREN;
				return;
			case ')':
				this.mark = RIGHT_PAREN;
				nextChar();
				return;

			case '.':
				// 在此处不支持数字,只支持a.b
				this.mark = Mark.DOT;
				nextChar();
				return;

			case '*':
				this.mark = Mark.ALL_COLUMN;
				nextChar();
				return;
			case '?':
				this.mark = Mark.VARIABLE;
				nextChar();
				return;

			default:
				if (Character.isLetter(this.$char)) {
					scanIdentifier();
					return;
				}

				if (isOperator(this.$char)) {
					scanOperator();
					return;
				}

				if (isEof()) {
					this.mark = EOF;
				} else {
					error();
					nextChar();
				}

				return;
			}
		}

	}

	public final Mark getMark() {
		return this.mark;
	}

	/**
	 * 扫描标识符
	 */
	public void scanIdentifier() {
		final char firstChar = this.$char;
		final boolean isFirstIdentifierChar = isFirstIdentifierChar(firstChar);
		if (!isFirstIdentifierChar) {
			throw new ParserException("illegal identifier");
		}
		this.bufferStartIndex = this.index;
		this.bufferSize = 1;
		char $char;
		while (true) {
			$char = charAt(++this.index);
			if (!isIdentifierChar($char)) {
				break;
			}
			this.bufferSize++;
		}

		this.stringValue = subString(this.bufferStartIndex, this.bufferSize);
		Mark mark = MarkMap.MAP.get(this.stringValue);
		if (mark != null) {
			this.mark = mark;
		} else {
			this.mark = Mark.IDENTIFIER;
		}
		this.$char = charAt(this.index);
	}

	/**
	 * 扫描\'xyz\'
	 */
	protected void scanString() {
		/**
		 * 上一个字符是'
		 */
		check(Mark.STRING_START);
		this.bufferStartIndex = this.index;
		this.bufferSize = 0;
		for (;;) {
			if (isEof()) {
				error();
				return;
			}
			this.$char = charAt(++this.index);
			if (this.$char == '\"') {
				throw new UnsupportedOperationException("不支持\"");
			} else if (this.$char == '\'') {
				this.mark = Mark.STRING_END;
				this.mark = Mark.STRING;
				nextChar();
				break;
			} else {
				this.bufferSize++;
			}

		}

		this.stringValue = subString(this.bufferStartIndex + 1, this.bufferSize);
	}

	public final boolean isDigit(char $char) {
		return $char >= '0' && $char <= '9';
	}

	public void scanNumber() {
		if (!(this.$char != '-' || isDigit(this.$char))) {
			throw new IllegalArgumentException("not number or -");
		}
		this.bufferSize = 0;// init
		//
		this.bufferStartIndex = this.index;
		//
		if (this.$char == '-') {
			this.bufferSize = 1;// 循环计数修正
			this.$char = charAt(++this.index);
		}

		while (true) {
			if (this.$char >= '0' && this.$char <= '9') {
				// 如果没有-则是从此开始计数
				this.bufferSize++;
			} else {
				break;
			}
			this.$char = charAt(++this.index);
		}

		boolean isDecimal = false;

		if (this.$char == '.') {
			if (!isDigit(charAt(this.index + 1))) {
				throw new IllegalArgumentException("not number");
			}
			isDecimal = true;
			this.bufferSize++;
			this.$char = charAt(++this.index);
			while (true) {
				if (this.$char >= '0' && this.$char <= '9') {
					this.bufferSize++;
				} else {
					break;
				}
				this.$char = charAt(++this.index);

			}
		}

		if (isDecimal) {
			this.mark = Mark.DECIMAL;
		} else {
			this.mark = Mark.INT;
		}
	}

	public BigDecimal decimalValue() {
		return new BigDecimal(subString(this.bufferStartIndex, this.bufferSize).toCharArray());
	}

	public String intOrDecimalValue() {
		return subString(this.bufferStartIndex, this.bufferSize);
	}

	private boolean isOperator(char $char) {
		switch ($char) {
		case '+':
			// ~代替-
		case '~':
			//
		case '!':
		case '=':
		case '>':
		case '<':
			return true;
		default:
			return false;
		}
	}

	private final void scanOperator() {
		switch (this.$char) {
		case '+':
			this.mark = Mark.ADD;
			nextChar();
			break;
		case '~':
			this.mark = Mark.SUB;
			nextChar();
			break;
		//
		case '!':
			nextChar();
			if (this.$char != '=') {
				throw new IllegalArgumentException("只支持!=");

			}
			this.mark = Mark.NotEqual;
			nextChar();
			break;

		case '=':
			this.mark = Mark.Equal;
			nextChar();
			break;
		case '>':
			this.mark = Mark.GreaterThan;
			nextChar();
			if (this.$char == '=') {
				this.mark = Mark.GreaterThanOrEqual;
				nextChar();
			}
			break;
		case '<':
			this.mark = Mark.LessThan;
			nextChar();
			if (this.$char == '=') {
				this.mark = Mark.LessThanOrEqual;
				nextChar();
			}
			break;
		default:
			throw new ParserException("else");
		}
	}

	public final int index() {
		return this.index;
	}

	public final String stringValue() {
		return this.stringValue;
	}

	/**
	 * 假定整个都为负数
	 */
	private static final long MIN_VALUE_DIVIDED_BY_TEN = Long.MIN_VALUE / 10;
	private static final long MAX_VALUE_DIVIDED_BY_TEN = -Long.MAX_VALUE / 10;
	private final static int[] DIGITS = new int[(int) '9' + 1];

	static {
		/**
		 * 字符对应数字
		 */
		for (int i = '0'; i <= '9'; ++i) {
			DIGITS[i] = i - '0';
		}
	}

	public final String numberString() {
		return subString(this.bufferStartIndex, this.bufferSize);
	}

	//
	public Number intValue() {
		int index = this.bufferStartIndex;
		int end = this.bufferStartIndex + this.bufferSize;
		//
		long limit;// 界限
		boolean negative;// 负数?
		//
		// Accumulating negatively avoids surprises near MAX_VALUE
		if (charAt(this.bufferStartIndex) == '-') {
			negative = true;
			limit = Long.MIN_VALUE;
			index++;
		} else {
			negative = false;
			limit = -Long.MAX_VALUE;
		}
		long dividedByTen = negative ? MAX_VALUE_DIVIDED_BY_TEN : MIN_VALUE_DIVIDED_BY_TEN;//

		long result = 0;
		int charValue;
		// 按照之后的负数进行计算
		if (index < end) {
			charValue = DIGITS[charAt(index++)];
			result = -charValue;
		}
		while (index < end) {
			result *= 10;
			charValue = DIGITS[charAt(index++)];
			// result-charValue可能会出现数字越界
			if (result < limit + charValue) {
				return new BigInteger(numberString());
			}
			result -= charValue;
			//
			if (result < dividedByTen) {
				return new BigInteger(numberString());
			}
		}
		/**
		 * 负数
		 */
		if (negative) {

			// int整数范围内
			if (result >= Integer.MIN_VALUE) {
				return (int) result;
			}
			return result;

			/**
			 * 正数
			 */
		} else {
			result = -result;
			// int整数范围内
			if (result <= Integer.MAX_VALUE) {
				return (int) result;
			}
			return result;
		}
	}

}
